﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml;

namespace is_alive
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Loaded += MainWindow_Loaded;
            Closed += MainWindow_Closed;
        }

        private void MainWindow_Closed(object sender, EventArgs e)
        {
            if (src != null)
            {
                if (src.Count > 0)
                {
                    write_current_list_to_xml(src);
                }
            }

            if (timer != null)
            {
                if (timer.IsEnabled == true)
                {
                    timer.Stop();
                }
            }
        }
        
        string xml_file_path = @"list.xml";
        private void write_current_list_to_xml(ObservableCollection<Is_Alive_class> src)
        {
            try
            {
                XmlTextWriter textWriter = new XmlTextWriter(xml_file_path, Encoding.UTF8);
                textWriter.Formatting = Formatting.Indented;
                textWriter.WriteStartDocument();
                textWriter.WriteStartElement("root");
                int i = 0;
                foreach (Is_Alive_class item in src)
                {
                    textWriter.WriteStartElement("node" + i.ToString()); i++;

                    textWriter.WriteStartElement("ip");
                    textWriter.WriteString(item.IP);
                    textWriter.WriteEndElement();

                    textWriter.WriteStartElement("note");
                    textWriter.WriteString(item.Note);
                    textWriter.WriteEndElement();

                    textWriter.WriteEndElement();
                }
                textWriter.WriteEndElement();
                //textWriter.WriteEndElement();
                textWriter.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        ObservableCollection<Is_Alive_class> src = new ObservableCollection<Is_Alive_class>();
        DispatcherTimer timer;
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //초기화
            src = new ObservableCollection<Is_Alive_class>();

            //과거 ip 리스트 갖고 오기.
            read_prev_xml_list(ref src);
            
            //소스 바인딩
            dataGrid.ItemsSource = src;

            //타이머 시작.
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(10);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private void read_prev_xml_list(ref ObservableCollection<Is_Alive_class> src)
        {
            try
            {
                if (!File.Exists(xml_file_path))
                {
                    return;
                }

                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(xml_file_path);

                XmlElement root = xmldoc.DocumentElement;
                XmlNodeList nodes = root.ChildNodes;

                foreach (XmlNode node in nodes)
                {
                    Is_Alive_class item = new Is_Alive_class();

                    item.Alived_PING = string.Empty;
                    item.Alived_ARP = "NO-ARP";

                    item.IP = node["ip"].InnerText;
                    item.Note = node["note"].InnerText;
                    item.LastTime = DateTime.Now.ToString();

                    src.Add(item);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            check_ip_is_alived(ref src);
        }

        private void check_ip_is_alived(ref ObservableCollection<Is_Alive_class> src)
        {
            try
            {
                for (int i = 0; i < src.Count; i++)
                {
                    string ip = src[i].IP;
                    Thread worker = new Thread(new ParameterizedThreadStart(run));
                    worker.Start(ip);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void run(object obj)
        {
            try
            {
                string ip = (string)obj;
                if (ip == null)
                {
                    return;
                }

                bool is_ping = get_alive_from_ip(ip);
                string is_arp = get_mac_by_ip(ip);
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
                {
                    var q = src.IndexOf(src.Where(x => x.IP == ip).FirstOrDefault());
                    if (q > -1)
                    {
                        src[q].Alived_PING = is_ping?"핑은 간다~":string.Empty;
                        src[q].Alived_ARP = is_arp;
                        src[q].LastTime = DateTime.Now.ToString();
                    }
                    else
                    {
                        //no item
                    }
                    UpdateLayout();
                }));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private string get_mac_by_ip(string ip)
        {
            //string ret = ip + " : " + NetworkUtils.GetMacAddress(IPAddress.Parse(ip)) + Environment.NewLine;
            string ret = NetworkUtils.GetMacAddress(IPAddress.Parse(ip)).ToString();

            if (ret == "000000000000")
            {
                ret = "NO-ARP";
            }

            return ret;
        }

        private bool get_alive_from_ip(string ip)
        {
            if (ip == null)
                return false;

            Ping pingSender = new Ping();
            PingReply reply = pingSender.Send(ip);
            if (reply.Status == IPStatus.Success)
                return true;
            else
                return false;
        }
    }

    class Is_Alive_class : INotifyPropertyChanged
    {
        private string alived_ping;
        public string Alived_PING
        {
            get { return alived_ping; }
            set { alived_ping = value;
                OnPropertyChanged("Alived_PING");
            }
        }

        private string alived_arp;
        public string Alived_ARP
        {
            get { return alived_arp; }
            set
            {
                alived_arp = value;
                OnPropertyChanged("Alived_ARP");
            }
        }

        private string ip;
        public string IP
        {
            get { return ip; }
            set {
                ip = value;
                OnPropertyChanged("IP");
            }
        }

        private string last_check_time;
        public string LastTime
        {
            get { return last_check_time; }
            set {
                last_check_time = value;
                OnPropertyChanged("LastTime");
            }
        }

        private string note;
        public string Note
        {
            get { return note; }
            set
            {
                note = value;
                OnPropertyChanged("Note");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public static class NetworkUtils
    {
        // http://www.codeproject.com/KB/IP/host_info_within_network.aspx
        [System.Runtime.InteropServices.DllImport("iphlpapi.dll", ExactSpelling = true)]
        static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref int PhyAddrLen);

        /// <summary>
        /// Gets the MAC address (<see cref="PhysicalAddress"/>) associated with the specified IP.
        /// </summary>
        /// <param name="ipAddress">The remote IP address.</param>
        /// <returns>The remote machine's MAC address.</returns>
        public static PhysicalAddress GetMacAddress(IPAddress ipAddress)
        {
            const int MacAddressLength = 6;
            int length = MacAddressLength;
            var macBytes = new byte[MacAddressLength];
            SendARP(BitConverter.ToInt32(ipAddress.GetAddressBytes(), 0), 0, macBytes, ref length);
            return new PhysicalAddress(macBytes);
        }
    }
}
